123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751 |
- # This file is dual licensed under the terms of the Apache License, Version
- # 2.0, and the BSD License. See the LICENSE file in the root of this repository
- # for complete details.
- from __future__ import absolute_import
- import distutils.util
- try:
- from importlib.machinery import EXTENSION_SUFFIXES
- except ImportError: # pragma: no cover
- import imp
- EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()]
- del imp
- import logging
- import os
- import platform
- import re
- import struct
- import sys
- import sysconfig
- import warnings
- from ._typing import TYPE_CHECKING, cast
- if TYPE_CHECKING: # pragma: no cover
- from typing import (
- Dict,
- FrozenSet,
- IO,
- Iterable,
- Iterator,
- List,
- Optional,
- Sequence,
- Tuple,
- Union,
- )
- PythonVersion = Sequence[int]
- MacVersion = Tuple[int, int]
- GlibcVersion = Tuple[int, int]
- logger = logging.getLogger(__name__)
- INTERPRETER_SHORT_NAMES = {
- "python": "py", # Generic.
- "cpython": "cp",
- "pypy": "pp",
- "ironpython": "ip",
- "jython": "jy",
- } # type: Dict[str, str]
- _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
- class Tag(object):
- """
- A representation of the tag triple for a wheel.
- Instances are considered immutable and thus are hashable. Equality checking
- is also supported.
- """
- __slots__ = ["_interpreter", "_abi", "_platform"]
- def __init__(self, interpreter, abi, platform):
- # type: (str, str, str) -> None
- self._interpreter = interpreter.lower()
- self._abi = abi.lower()
- self._platform = platform.lower()
- @property
- def interpreter(self):
- # type: () -> str
- return self._interpreter
- @property
- def abi(self):
- # type: () -> str
- return self._abi
- @property
- def platform(self):
- # type: () -> str
- return self._platform
- def __eq__(self, other):
- # type: (object) -> bool
- if not isinstance(other, Tag):
- return NotImplemented
- return (
- (self.platform == other.platform)
- and (self.abi == other.abi)
- and (self.interpreter == other.interpreter)
- )
- def __hash__(self):
- # type: () -> int
- return hash((self._interpreter, self._abi, self._platform))
- def __str__(self):
- # type: () -> str
- return "{}-{}-{}".format(self._interpreter, self._abi, self._platform)
- def __repr__(self):
- # type: () -> str
- return "<{self} @ {self_id}>".format(self=self, self_id=id(self))
- def parse_tag(tag):
- # type: (str) -> FrozenSet[Tag]
- """
- Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
- Returning a set is required due to the possibility that the tag is a
- compressed tag set.
- """
- tags = set()
- interpreters, abis, platforms = tag.split("-")
- for interpreter in interpreters.split("."):
- for abi in abis.split("."):
- for platform_ in platforms.split("."):
- tags.add(Tag(interpreter, abi, platform_))
- return frozenset(tags)
- def _warn_keyword_parameter(func_name, kwargs):
- # type: (str, Dict[str, bool]) -> bool
- """
- Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only.
- """
- if not kwargs:
- return False
- elif len(kwargs) > 1 or "warn" not in kwargs:
- kwargs.pop("warn", None)
- arg = next(iter(kwargs.keys()))
- raise TypeError(
- "{}() got an unexpected keyword argument {!r}".format(func_name, arg)
- )
- return kwargs["warn"]
- def _get_config_var(name, warn=False):
- # type: (str, bool) -> Union[int, str, None]
- value = sysconfig.get_config_var(name)
- if value is None and warn:
- logger.debug(
- "Config variable '%s' is unset, Python ABI tag may be incorrect", name
- )
- return value
- def _normalize_string(string):
- # type: (str) -> str
- return string.replace(".", "_").replace("-", "_")
- def _abi3_applies(python_version):
- # type: (PythonVersion) -> bool
- """
- Determine if the Python version supports abi3.
- PEP 384 was first implemented in Python 3.2.
- """
- return len(python_version) > 1 and tuple(python_version) >= (3, 2)
- def _cpython_abis(py_version, warn=False):
- # type: (PythonVersion, bool) -> List[str]
- py_version = tuple(py_version) # To allow for version comparison.
- abis = []
- version = _version_nodot(py_version[:2])
- debug = pymalloc = ucs4 = ""
- with_debug = _get_config_var("Py_DEBUG", warn)
- has_refcount = hasattr(sys, "gettotalrefcount")
- # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled
- # extension modules is the best option.
- # https://github.com/pypa/pip/issues/3383#issuecomment-173267692
- has_ext = "_d.pyd" in EXTENSION_SUFFIXES
- if with_debug or (with_debug is None and (has_refcount or has_ext)):
- debug = "d"
- if py_version < (3, 8):
- with_pymalloc = _get_config_var("WITH_PYMALLOC", warn)
- if with_pymalloc or with_pymalloc is None:
- pymalloc = "m"
- if py_version < (3, 3):
- unicode_size = _get_config_var("Py_UNICODE_SIZE", warn)
- if unicode_size == 4 or (
- unicode_size is None and sys.maxunicode == 0x10FFFF
- ):
- ucs4 = "u"
- elif debug:
- # Debug builds can also load "normal" extension modules.
- # We can also assume no UCS-4 or pymalloc requirement.
- abis.append("cp{version}".format(version=version))
- abis.insert(
- 0,
- "cp{version}{debug}{pymalloc}{ucs4}".format(
- version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4
- ),
- )
- return abis
- def cpython_tags(
- python_version=None, # type: Optional[PythonVersion]
- abis=None, # type: Optional[Iterable[str]]
- platforms=None, # type: Optional[Iterable[str]]
- **kwargs # type: bool
- ):
- # type: (...) -> Iterator[Tag]
- """
- Yields the tags for a CPython interpreter.
- The tags consist of:
- - cp<python_version>-<abi>-<platform>
- - cp<python_version>-abi3-<platform>
- - cp<python_version>-none-<platform>
- - cp<less than python_version>-abi3-<platform> # Older Python versions down to 3.2.
- If python_version only specifies a major version then user-provided ABIs and
- the 'none' ABItag will be used.
- If 'abi3' or 'none' are specified in 'abis' then they will be yielded at
- their normal position and not at the beginning.
- """
- warn = _warn_keyword_parameter("cpython_tags", kwargs)
- if not python_version:
- python_version = sys.version_info[:2]
- interpreter = "cp{}".format(_version_nodot(python_version[:2]))
- if abis is None:
- if len(python_version) > 1:
- abis = _cpython_abis(python_version, warn)
- else:
- abis = []
- abis = list(abis)
- # 'abi3' and 'none' are explicitly handled later.
- for explicit_abi in ("abi3", "none"):
- try:
- abis.remove(explicit_abi)
- except ValueError:
- pass
- platforms = list(platforms or _platform_tags())
- for abi in abis:
- for platform_ in platforms:
- yield Tag(interpreter, abi, platform_)
- if _abi3_applies(python_version):
- for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms):
- yield tag
- for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms):
- yield tag
- if _abi3_applies(python_version):
- for minor_version in range(python_version[1] - 1, 1, -1):
- for platform_ in platforms:
- interpreter = "cp{version}".format(
- version=_version_nodot((python_version[0], minor_version))
- )
- yield Tag(interpreter, "abi3", platform_)
- def _generic_abi():
- # type: () -> Iterator[str]
- abi = sysconfig.get_config_var("SOABI")
- if abi:
- yield _normalize_string(abi)
- def generic_tags(
- interpreter=None, # type: Optional[str]
- abis=None, # type: Optional[Iterable[str]]
- platforms=None, # type: Optional[Iterable[str]]
- **kwargs # type: bool
- ):
- # type: (...) -> Iterator[Tag]
- """
- Yields the tags for a generic interpreter.
- The tags consist of:
- - <interpreter>-<abi>-<platform>
- The "none" ABI will be added if it was not explicitly provided.
- """
- warn = _warn_keyword_parameter("generic_tags", kwargs)
- if not interpreter:
- interp_name = interpreter_name()
- interp_version = interpreter_version(warn=warn)
- interpreter = "".join([interp_name, interp_version])
- if abis is None:
- abis = _generic_abi()
- platforms = list(platforms or _platform_tags())
- abis = list(abis)
- if "none" not in abis:
- abis.append("none")
- for abi in abis:
- for platform_ in platforms:
- yield Tag(interpreter, abi, platform_)
- def _py_interpreter_range(py_version):
- # type: (PythonVersion) -> Iterator[str]
- """
- Yields Python versions in descending order.
- After the latest version, the major-only version will be yielded, and then
- all previous versions of that major version.
- """
- if len(py_version) > 1:
- yield "py{version}".format(version=_version_nodot(py_version[:2]))
- yield "py{major}".format(major=py_version[0])
- if len(py_version) > 1:
- for minor in range(py_version[1] - 1, -1, -1):
- yield "py{version}".format(version=_version_nodot((py_version[0], minor)))
- def compatible_tags(
- python_version=None, # type: Optional[PythonVersion]
- interpreter=None, # type: Optional[str]
- platforms=None, # type: Optional[Iterable[str]]
- ):
- # type: (...) -> Iterator[Tag]
- """
- Yields the sequence of tags that are compatible with a specific version of Python.
- The tags consist of:
- - py*-none-<platform>
- - <interpreter>-none-any # ... if `interpreter` is provided.
- - py*-none-any
- """
- if not python_version:
- python_version = sys.version_info[:2]
- platforms = list(platforms or _platform_tags())
- for version in _py_interpreter_range(python_version):
- for platform_ in platforms:
- yield Tag(version, "none", platform_)
- if interpreter:
- yield Tag(interpreter, "none", "any")
- for version in _py_interpreter_range(python_version):
- yield Tag(version, "none", "any")
- def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
- # type: (str, bool) -> str
- if not is_32bit:
- return arch
- if arch.startswith("ppc"):
- return "ppc"
- return "i386"
- def _mac_binary_formats(version, cpu_arch):
- # type: (MacVersion, str) -> List[str]
- formats = [cpu_arch]
- if cpu_arch == "x86_64":
- if version < (10, 4):
- return []
- formats.extend(["intel", "fat64", "fat32"])
- elif cpu_arch == "i386":
- if version < (10, 4):
- return []
- formats.extend(["intel", "fat32", "fat"])
- elif cpu_arch == "ppc64":
- # TODO: Need to care about 32-bit PPC for ppc64 through 10.2?
- if version > (10, 5) or version < (10, 4):
- return []
- formats.append("fat64")
- elif cpu_arch == "ppc":
- if version > (10, 6):
- return []
- formats.extend(["fat32", "fat"])
- formats.append("universal")
- return formats
- def mac_platforms(version=None, arch=None):
- # type: (Optional[MacVersion], Optional[str]) -> Iterator[str]
- """
- Yields the platform tags for a macOS system.
- The `version` parameter is a two-item tuple specifying the macOS version to
- generate platform tags for. The `arch` parameter is the CPU architecture to
- generate platform tags for. Both parameters default to the appropriate value
- for the current system.
- """
- version_str, _, cpu_arch = platform.mac_ver() # type: ignore
- if version is None:
- version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
- else:
- version = version
- if arch is None:
- arch = _mac_arch(cpu_arch)
- else:
- arch = arch
- for minor_version in range(version[1], -1, -1):
- compat_version = version[0], minor_version
- binary_formats = _mac_binary_formats(compat_version, arch)
- for binary_format in binary_formats:
- yield "macosx_{major}_{minor}_{binary_format}".format(
- major=compat_version[0],
- minor=compat_version[1],
- binary_format=binary_format,
- )
- # From PEP 513.
- def _is_manylinux_compatible(name, glibc_version):
- # type: (str, GlibcVersion) -> bool
- # Check for presence of _manylinux module.
- try:
- import _manylinux # noqa
- return bool(getattr(_manylinux, name + "_compatible"))
- except (ImportError, AttributeError):
- # Fall through to heuristic check below.
- pass
- return _have_compatible_glibc(*glibc_version)
- def _glibc_version_string():
- # type: () -> Optional[str]
- # Returns glibc version string, or None if not using glibc.
- return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
- def _glibc_version_string_confstr():
- # type: () -> Optional[str]
- """
- Primary implementation of glibc_version_string using os.confstr.
- """
- # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
- # to be broken or missing. This strategy is used in the standard library
- # platform module.
- # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183
- try:
- # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
- version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821
- "CS_GNU_LIBC_VERSION"
- )
- assert version_string is not None
- _, version = version_string.split() # type: Tuple[str, str]
- except (AssertionError, AttributeError, OSError, ValueError):
- # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
- return None
- return version
- def _glibc_version_string_ctypes():
- # type: () -> Optional[str]
- """
- Fallback implementation of glibc_version_string using ctypes.
- """
- try:
- import ctypes
- except ImportError:
- return None
- # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
- # manpage says, "If filename is NULL, then the returned handle is for the
- # main program". This way we can let the linker do the work to figure out
- # which libc our process is actually using.
- #
- # Note: typeshed is wrong here so we are ignoring this line.
- process_namespace = ctypes.CDLL(None) # type: ignore
- try:
- gnu_get_libc_version = process_namespace.gnu_get_libc_version
- except AttributeError:
- # Symbol doesn't exist -> therefore, we are not linked to
- # glibc.
- return None
- # Call gnu_get_libc_version, which returns a string like "2.5"
- gnu_get_libc_version.restype = ctypes.c_char_p
- version_str = gnu_get_libc_version() # type: str
- # py2 / py3 compatibility:
- if not isinstance(version_str, str):
- version_str = version_str.decode("ascii")
- return version_str
- # Separated out from have_compatible_glibc for easier unit testing.
- def _check_glibc_version(version_str, required_major, minimum_minor):
- # type: (str, int, int) -> bool
- # Parse string and check against requested version.
- #
- # We use a regexp instead of str.split because we want to discard any
- # random junk that might come after the minor version -- this might happen
- # in patched/forked versions of glibc (e.g. Linaro's version of glibc
- # uses version strings like "2.20-2014.11"). See gh-3588.
- m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
- if not m:
- warnings.warn(
- "Expected glibc version with 2 components major.minor,"
- " got: %s" % version_str,
- RuntimeWarning,
- )
- return False
- return (
- int(m.group("major")) == required_major
- and int(m.group("minor")) >= minimum_minor
- )
- def _have_compatible_glibc(required_major, minimum_minor):
- # type: (int, int) -> bool
- version_str = _glibc_version_string()
- if version_str is None:
- return False
- return _check_glibc_version(version_str, required_major, minimum_minor)
- # Python does not provide platform information at sufficient granularity to
- # identify the architecture of the running executable in some cases, so we
- # determine it dynamically by reading the information from the running
- # process. This only applies on Linux, which uses the ELF format.
- class _ELFFileHeader(object):
- # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
- class _InvalidELFFileHeader(ValueError):
- """
- An invalid ELF file header was found.
- """
- ELF_MAGIC_NUMBER = 0x7F454C46
- ELFCLASS32 = 1
- ELFCLASS64 = 2
- ELFDATA2LSB = 1
- ELFDATA2MSB = 2
- EM_386 = 3
- EM_S390 = 22
- EM_ARM = 40
- EM_X86_64 = 62
- EF_ARM_ABIMASK = 0xFF000000
- EF_ARM_ABI_VER5 = 0x05000000
- EF_ARM_ABI_FLOAT_HARD = 0x00000400
- def __init__(self, file):
- # type: (IO[bytes]) -> None
- def unpack(fmt):
- # type: (str) -> int
- try:
- (result,) = struct.unpack(
- fmt, file.read(struct.calcsize(fmt))
- ) # type: (int, )
- except struct.error:
- raise _ELFFileHeader._InvalidELFFileHeader()
- return result
- self.e_ident_magic = unpack(">I")
- if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_class = unpack("B")
- if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_data = unpack("B")
- if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_version = unpack("B")
- self.e_ident_osabi = unpack("B")
- self.e_ident_abiversion = unpack("B")
- self.e_ident_pad = file.read(7)
- format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
- format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
- format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
- format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
- self.e_type = unpack(format_h)
- self.e_machine = unpack(format_h)
- self.e_version = unpack(format_i)
- self.e_entry = unpack(format_p)
- self.e_phoff = unpack(format_p)
- self.e_shoff = unpack(format_p)
- self.e_flags = unpack(format_i)
- self.e_ehsize = unpack(format_h)
- self.e_phentsize = unpack(format_h)
- self.e_phnum = unpack(format_h)
- self.e_shentsize = unpack(format_h)
- self.e_shnum = unpack(format_h)
- self.e_shstrndx = unpack(format_h)
- def _get_elf_header():
- # type: () -> Optional[_ELFFileHeader]
- try:
- with open(sys.executable, "rb") as f:
- elf_header = _ELFFileHeader(f)
- except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
- return None
- return elf_header
- def _is_linux_armhf():
- # type: () -> bool
- # hard-float ABI can be detected from the ELF header of the running
- # process
- # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
- elf_header = _get_elf_header()
- if elf_header is None:
- return False
- result = elf_header.e_ident_class == elf_header.ELFCLASS32
- result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
- result &= elf_header.e_machine == elf_header.EM_ARM
- result &= (
- elf_header.e_flags & elf_header.EF_ARM_ABIMASK
- ) == elf_header.EF_ARM_ABI_VER5
- result &= (
- elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD
- ) == elf_header.EF_ARM_ABI_FLOAT_HARD
- return result
- def _is_linux_i686():
- # type: () -> bool
- elf_header = _get_elf_header()
- if elf_header is None:
- return False
- result = elf_header.e_ident_class == elf_header.ELFCLASS32
- result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
- result &= elf_header.e_machine == elf_header.EM_386
- return result
- def _have_compatible_manylinux_abi(arch):
- # type: (str) -> bool
- if arch == "armv7l":
- return _is_linux_armhf()
- if arch == "i686":
- return _is_linux_i686()
- return True
- def _linux_platforms(is_32bit=_32_BIT_INTERPRETER):
- # type: (bool) -> Iterator[str]
- linux = _normalize_string(distutils.util.get_platform())
- if is_32bit:
- if linux == "linux_x86_64":
- linux = "linux_i686"
- elif linux == "linux_aarch64":
- linux = "linux_armv7l"
- manylinux_support = []
- _, arch = linux.split("_", 1)
- if _have_compatible_manylinux_abi(arch):
- if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}:
- manylinux_support.append(
- ("manylinux2014", (2, 17))
- ) # CentOS 7 w/ glibc 2.17 (PEP 599)
- if arch in {"x86_64", "i686"}:
- manylinux_support.append(
- ("manylinux2010", (2, 12))
- ) # CentOS 6 w/ glibc 2.12 (PEP 571)
- manylinux_support.append(
- ("manylinux1", (2, 5))
- ) # CentOS 5 w/ glibc 2.5 (PEP 513)
- manylinux_support_iter = iter(manylinux_support)
- for name, glibc_version in manylinux_support_iter:
- if _is_manylinux_compatible(name, glibc_version):
- yield linux.replace("linux", name)
- break
- # Support for a later manylinux implies support for an earlier version.
- for name, _ in manylinux_support_iter:
- yield linux.replace("linux", name)
- yield linux
- def _generic_platforms():
- # type: () -> Iterator[str]
- yield _normalize_string(distutils.util.get_platform())
- def _platform_tags():
- # type: () -> Iterator[str]
- """
- Provides the platform tags for this installation.
- """
- if platform.system() == "Darwin":
- return mac_platforms()
- elif platform.system() == "Linux":
- return _linux_platforms()
- else:
- return _generic_platforms()
- def interpreter_name():
- # type: () -> str
- """
- Returns the name of the running interpreter.
- """
- try:
- name = sys.implementation.name # type: ignore
- except AttributeError: # pragma: no cover
- # Python 2.7 compatibility.
- name = platform.python_implementation().lower()
- return INTERPRETER_SHORT_NAMES.get(name) or name
- def interpreter_version(**kwargs):
- # type: (bool) -> str
- """
- Returns the version of the running interpreter.
- """
- warn = _warn_keyword_parameter("interpreter_version", kwargs)
- version = _get_config_var("py_version_nodot", warn=warn)
- if version:
- version = str(version)
- else:
- version = _version_nodot(sys.version_info[:2])
- return version
- def _version_nodot(version):
- # type: (PythonVersion) -> str
- if any(v >= 10 for v in version):
- sep = "_"
- else:
- sep = ""
- return sep.join(map(str, version))
- def sys_tags(**kwargs):
- # type: (bool) -> Iterator[Tag]
- """
- Returns the sequence of tag triples for the running interpreter.
- The order of the sequence corresponds to priority order for the
- interpreter, from most to least important.
- """
- warn = _warn_keyword_parameter("sys_tags", kwargs)
- interp_name = interpreter_name()
- if interp_name == "cp":
- for tag in cpython_tags(warn=warn):
- yield tag
- else:
- for tag in generic_tags():
- yield tag
- for tag in compatible_tags():
- yield tag
|